<?php

use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Pterodactyl\Models\Permission;
use Illuminate\Support\Facades\Schema;
use Pterodactyl\Models\Permission as P;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class MergePermissionsTableIntoSubusers extends Migration
{
  /**
   * A list of all pre-1.0 permissions available to a user and their associated
   * casting for the new permissions system.
   *
   * @var array
   */
  protected static $permissionsMap = [
    'power-start' => P::ACTION_CONTROL_START,
    'power-stop' => P::ACTION_CONTROL_STOP,
    'power-restart' => P::ACTION_CONTROL_RESTART,
    'power-kill' => P::ACTION_CONTROL_STOP,
    'send-command' => P::ACTION_CONTROL_CONSOLE,
    'list-subusers' => P::ACTION_USER_READ,
    'view-subuser' => P::ACTION_USER_READ,
    'edit-subuser' => P::ACTION_USER_UPDATE,
    'create-subuser' => P::ACTION_USER_CREATE,
    'delete-subuser' => P::ACTION_USER_DELETE,
    'view-allocations' => P::ACTION_ALLOCATION_READ,
    'edit-allocation' => P::ACTION_ALLOCATION_UPDATE,
    'view-startup' => P::ACTION_STARTUP_READ,
    'edit-startup' => P::ACTION_STARTUP_UPDATE,
    'view-databases' => P::ACTION_DATABASE_READ,
    // Better to just break this flow a bit than accidentally grant a dangerous permission.
    'reset-db-password' => P::ACTION_DATABASE_UPDATE,
    'delete-database' => P::ACTION_DATABASE_DELETE,
    'create-database' => P::ACTION_DATABASE_CREATE,
    'access-sftp' => P::ACTION_FILE_SFTP,
    'list-files' => P::ACTION_FILE_READ,
    'edit-files' => P::ACTION_FILE_READ_CONTENT,
    'save-files' => P::ACTION_FILE_UPDATE,
    'create-files' => P::ACTION_FILE_CREATE,
    'delete-files' => P::ACTION_FILE_DELETE,
    'compress-files' => P::ACTION_FILE_ARCHIVE,
    'list-schedules' => P::ACTION_SCHEDULE_READ,
    'view-schedule' => P::ACTION_SCHEDULE_READ,
    'edit-schedule' => P::ACTION_SCHEDULE_UPDATE,
    'create-schedule' => P::ACTION_SCHEDULE_CREATE,
    'delete-schedule' => P::ACTION_SCHEDULE_DELETE,
    // Skipping these permissions as they are granted if you have more specific read/write permissions.
    'move-files' => null,
    'copy-files' => null,
    'decompress-files' => null,
    'upload-files' => null,
    'download-files' => null,
    // These permissions do not exist in 1.0
    'toggle-schedule' => null,
    'queue-schedule' => null,
  ];

  /**
   * Run the migrations.
   */
  public function up(): void
  {
    Schema::table('subusers', function (Blueprint $table) {
      $table->json('permissions')->nullable()->after('server_id');
    });

    $cursor = DB::table('permissions')
      ->select(['subuser_id'])
      ->when(DB::getPdo()->getAttribute(PDO::ATTR_DRIVER_NAME) === 'mysql', function ($query) {
        $query->selectRaw('group_concat(permission) as permissions');
      })
      ->when(DB::getPdo()->getAttribute(PDO::ATTR_DRIVER_NAME) === 'pgsql', function ($query) {
        $query->selectRaw("string_agg(permission, ',') as permissions");
      })
      ->from('permissions')
      ->groupBy(['subuser_id'])
      ->cursor();

    DB::transaction(function () use (&$cursor) {
      $cursor->each(function ($datum) {
        $updated = Collection::make(explode(',', $datum->permissions))
          ->map(function ($value) {
            return self::$permissionsMap[$value] ?? null;
          })->filter(function ($value) {
            return !is_null($value) && $value !== Permission::ACTION_WEBSOCKET_CONNECT;
          })
          // All subusers get this permission, so make sure it gets pushed into the array.
          ->merge([Permission::ACTION_WEBSOCKET_CONNECT])
          ->unique()
          ->values()
          ->toJson();

        DB::update('UPDATE subusers SET permissions = ? WHERE id = ?', [$updated, $datum->subuser_id]);
      });
    });
  }

  /**
   * Reverse the migrations.
   */
  public function down(): void
  {
    $flipped = array_flip(array_filter(self::$permissionsMap));

    foreach (DB::select('SELECT id, permissions FROM subusers') as $datum) {
      $values = [];
      foreach (json_decode($datum->permissions, true) as $permission) {
        $v = $flipped[$permission] ?? null;
        if (!empty($v)) {
          $values[] = $datum->id;
          $values[] = $v;
        }
      }

      if (!empty($values)) {
        $string = 'VALUES ' . implode(', ', array_fill(0, count($values) / 2, '(?, ?)'));

        DB::insert('INSERT INTO permissions(`subuser_id`, `permission`) ' . $string, $values);
      }
    }

    Schema::table('subusers', function (Blueprint $table) {
      $table->dropColumn('permissions');
    });
  }
}
